﻿using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.BizTalk.Component.Interop;
using System.IO;
using System.Globalization;
using log4net;
using AppFx.BizTalk.Diagnostics;

namespace AppFx.BizTalk.PipelineComponents
{
    /// <summary>
    /// Pipeline component which can log and hide exception details on the response side of a request response port so detailed exceptions wouldnt be thrown and expose a security risk
    /// </summary>
    [ComponentCategory(CategoryTypes.CATID_PipelineComponent)]
    [ComponentCategory(CategoryTypes.CATID_Any)]
    [System.Runtime.InteropServices.Guid(ComponentGuid)]
    public class ResponseExceptionHiding : BaseCustomPipelineComponent, IComponent, IComponentUI, IPersistPropertyBag
    {
        private static readonly ILog Log = LogFactory.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
        private const string DefaultEventSource = "BizTalk Exception Hiding";
        private const string ComponentGuid = "20043687-8E1E-47E3-AE69-30E782F090E5";
        private const string ComponentDescription = "Protects BizTalk from exposing internal sensitive exception information";
        private const string ComponentName = "Response Exception Hiding";
                
        /// <summary>
        /// Ctor
        /// </summary>
        public ResponseExceptionHiding() : base(ComponentName, typeof(ResponseExceptionHiding), ComponentDescription)
        {

        }

        #region IComponent Members
        /// <summary>
        /// Processing of the component
        /// </summary>
        /// <param name="pContext"></param>
        /// <param name="pInMsg"></param>
        /// <returns></returns>
        public Microsoft.BizTalk.Message.Interop.IBaseMessage Execute(IPipelineContext pContext, Microsoft.BizTalk.Message.Interop.IBaseMessage pInMsg)
        {
            const string NACKType = "NACK";

            if (pInMsg == null)
                throw new ArgumentNullException("pInMsg");

            if (base.ReadContextPropertyAsString(pInMsg.Context, new BTS.AckType()) == NACKType)
            {
                //Log Error
                string errorId = Guid.NewGuid().ToString();
                string errorDescription = base.ReadContextPropertyAsString(pInMsg.Context, new BTS.AckDescription());
                string instanceId = pInMsg.MessageID.ToString();
                string logMessage = string.Format("Error Masked by ResponseExceptionHiding pipeline component\r\nBizTalk Instance: {0}\r\nClient Error ID: {1}\r\nError Description: {2}", instanceId, errorId, errorDescription);
                
                Log.Error(logMessage, null);


                //Replace Message
                MemoryStream memoryStream = new MemoryStream();
                StreamWriter writer = new StreamWriter(memoryStream);
                writer.Write(string.Format("An error has occured and has been logged by BizTalk.  Please refer to support with the following details.\r\nError ID: {0}\r\nInstance ID: {1}", errorId, instanceId));
                writer.Flush();
                memoryStream.Seek(0, SeekOrigin.Begin);

                pInMsg.BodyPart.Data = memoryStream;
            }

            return pInMsg;
        }
        #endregion

        #region Properties
        private string _telemetryClientName;
        private string _loggerName;

        public string LoggerName
        {
            get { return _loggerName; }
            set { _loggerName = value; }
        }

        public string TelemetryClientName
        {
            get { return _telemetryClientName; }
            set { _telemetryClientName = value; }
        }
        #endregion

        #region IComponentUI Members
        /// <summary>
        /// Icon
        /// </summary>
        public IntPtr Icon
        {
            get { return IntPtr.Zero; }
        }
        /// <summary>
        /// Validate
        /// </summary>
        /// <param name="projectSystem"></param>
        /// <returns></returns>
        public System.Collections.IEnumerator Validate(object projectSystem)
        {
            return null;
        }
        #endregion

        #region IPersistPropertyBag Members
        /// <summary>
        /// The guid
        /// </summary>
        /// <param name="classID"></param>
        public void GetClassID(out Guid classID)
        {
            classID = new Guid(ComponentGuid);
        }
        /// <summary>
        /// No implementation
        /// </summary>
        public void InitNew()
        {
        }
        /// <summary>
        /// Loads from property bag
        /// </summary>
        /// <param name="propertyBag"></param>
        /// <param name="errorLog"></param>        
        public void Load(IPropertyBag propertyBag, int errorLog)
        {
            try
            {
                base.ReadPropertyBag(propertyBag, "TelemetryClientName", ref this._telemetryClientName);
                base.ReadPropertyBag(propertyBag, "LoggerName", ref this._loggerName);                
            }
            catch (Exception ex)
            {
                string message = string.Format(CultureInfo.InvariantCulture, "IPersistPropertyBag.Load error: {0}", ex.ToString());
                Log.Error(message, ex);
                throw;
            }
        }
        /// <summary>
        /// Sdaves state to property bag
        /// </summary>
        /// <param name="propertyBag"></param>
        /// <param name="clearDirty"></param>
        /// <param name="saveAllProperties"></param>
        public void Save(IPropertyBag propertyBag, bool clearDirty, bool saveAllProperties)
        {
            try
            {
                base.WritePropertyBag(propertyBag, "TelemetryClientName", this._telemetryClientName);
                base.WritePropertyBag(propertyBag, "LoggerName", this._loggerName);
            }
            catch (Exception ex)
            {
                string message = string.Format(CultureInfo.InvariantCulture, "IPersistPropertyBag.Load error: {0}", ex.ToString());
                Log.Error(message, ex);
                throw;
            }
        }
        #endregion
    }
}

